package edu.indiana.cs.b534.torrent.impl;

import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;

import edu.indiana.cs.b534.torrent.Constants;
import edu.indiana.cs.b534.torrent.StorageManager;
import edu.indiana.cs.b534.torrent.TorrentException;
import edu.indiana.cs.b534.torrent.context.PeerDescription;

public class RequestPipeline {
	private static Logger logger = Logger.getLogger(TorrentManager.TORRENT_MANAGER_NAME);
	
	private int pipelineSize;
	private final PeerInstance peer;
	private final Map<Integer, Long> piecesBeingDownloaded;
	private int nextBlockOffset = 0;
	private int pieceIndex;
	//private final TorrentContext torrentContext;
	private final StorageManager storageManager;
	
	
	
	public RequestPipeline(int pipelineSize, PeerInstance peer) {
		this.pipelineSize = pipelineSize;
		this.peer = peer;
		piecesBeingDownloaded = new HashMap<Integer, Long>(pipelineSize);
		this.storageManager = peer.getStorageManager();
	}

	public synchronized void startDownloadPiece(int pieceIndex) throws TorrentException{
		reset();
		this.pieceIndex = pieceIndex;
		PieceInfo pieceInfo = storageManager.getPieceInfo(pieceIndex);
		for(int i = 0;i<pipelineSize;i++){
            int amountLeftToDownload = pieceInfo.getPieceLength() - nextBlockOffset;
           if (amountLeftToDownload > 0) {
        	   downloadNextBlock();
           }else{
        	   break;
           }
		}
	}
	
	private void downloadNextBlock() throws TorrentException{
		PieceInfo pieceInfo = storageManager.getPieceInfo(pieceIndex);
        int amountLeftToDownload = pieceInfo.getPieceLength() - nextBlockOffset;
           if (amountLeftToDownload > 0) {

               logger.info(amountLeftToDownload + "/" + pieceInfo.getPieceLength() + " left to be downloaded in piece " + pieceIndex);

               int amountToDownLoadNext = Math.min(Constants.BLOCK_SIZE, amountLeftToDownload);
               peer.sendRequestMessage(pieceIndex, nextBlockOffset, amountToDownLoadNext);
           } else {
               throw new TorrentException("I don't have anything to download (amount left " +
                       "to be downloaded = " + amountLeftToDownload + "). But I was asked by " +
                       "Storage Manager to download more data for the piece " + pieceIndex);
           }
           piecesBeingDownloaded.put(nextBlockOffset, System.currentTimeMillis());
           nextBlockOffset = nextBlockOffset + Constants.BLOCK_SIZE;
	}
	
	public synchronized void notifyBlockDownloaded(int pieceIndex,int begin,byte[] data,int srcOffset) throws TorrentException{
		if(pieceIndex != this.pieceIndex ){
			throw new TorrentException("I am downloading piece "+this.pieceIndex + " but I recived a block for piece "+ pieceIndex);
		}
		
//		
		PeerDescription peerDescription = peer.getPeerDescription();
        // save the piece
        storageManager.storeBlock(pieceIndex, begin, data, srcOffset);
        piecesBeingDownloaded.remove(new Integer(begin));
        peerDescription.setTimeLastRequestOrInterstedSent(-1);
        // check whether you have downloaded the whole thing, say the piece, that you are
        // supposed to download
        if (!storageManager.isPieceCompleted(pieceIndex)) {
        	PieceInfo pieceInfo = storageManager.getPieceInfo(pieceIndex);

        	if(peer.getManager().getTorrentContext().isPipeLineInBatch()){
        		if(piecesBeingDownloaded.size() == 0){
            		for(int i = 0;i<pipelineSize;i++){
                       int amountLeftToDownload = pieceInfo.getPieceLength() - nextBlockOffset;
                       if (amountLeftToDownload > 0) {
                    	   downloadNextBlock();
                       }else{
                    	   break;
                       }
            		}
        		}
        	}else{
                int amountLeftToDownload = pieceInfo.getPieceLength() - nextBlockOffset;
                if (amountLeftToDownload > 0) {
             	   downloadNextBlock();
                }else if(piecesBeingDownloaded.size() > 0){
             	   logger.info("All requests sent, "+ piecesBeingDownloaded.size() + " pieces to be arrived before complete");
                }else{
             	   throw new TorrentException("I don't have anything to download (amount left " +
                            "to be downloaded = " + amountLeftToDownload + "). But I was asked by " +
                            "Storage Manager to download more data for the piece " + pieceIndex);
                }
        	}
        } else {
        	PieceManager manager = peer.getManager();
            manager.setPieceDownloaded(manager.getPieceInfo(pieceIndex), peerDescription.getPeerId());
            peer.resetBusyState();
        }
        logger.info("Piece Message " + pieceIndex + " offset " + begin + " is saved");
        
	}
	
	
	public synchronized void reset(){
		piecesBeingDownloaded.clear();
		this.pieceIndex = -1;
		nextBlockOffset = 0;
	}
	//public void 
	

}
